package com.citywithincity.api.post.impl;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.citywithincity.api.post.ApiPost;
import com.citywithincity.api.post.ApiResponse;
import com.citywithincity.utils.JsonUtil;
import com.citywithincity.utils.MD5Util;
import com.damai.annotation.Crypt;

public class ShareApiPost implements ApiPost {

	private static Log log = LogFactory.getLog(ShareApiPost.class);

	private String baseUrl;
	private String phone;
	private String token;

	private String publicKey;
	private String signKey;
	private String refreshToken;
	private String deviceId;
	
	public ShareApiPost(String baseUrl,String phone){
		this.baseUrl = baseUrl;
		this.phone =phone;
		this.deviceId = MD5Util.md5Appkey(phone);
	}

	public Object post(String api) throws IOException{
		return post(api, null,Crypt.NONE);
	}
	
	public Object post(String api, Map<String, Object> jsonObject) throws IOException{
		return post(api, jsonObject,Crypt.NONE);
	}
	
	@SuppressWarnings("unchecked")
	public ApiResponse post(String api, Map<String, Object> args, int crypt) throws IOException {
		Object content = rawPost(api, args, crypt);
		
		if (content instanceof Map) {
			Map<String, Object> map = (Map<String, Object>)content;

			String http_status = map.get("http_status")+"";
			if (!"".equals(http_status) && !"null".equals(http_status)) {
				log.info("http_status:"+http_status);
				if (http_status.equals("401")) {
					login();
					post(api, args, crypt);
				} else if (http_status.equals("403")) {
					requestToken();
					post(api, args, crypt);
				}
			}
			Object errMsg = map.get("errMsg");
			if (errMsg != null) {
				log.info("errMsg:"+errMsg);
				Map<String, Object> json = new HashMap<String, Object>();
				json.put("flag", -1);
				json.put("result", errMsg);
				return new ApiResponse(json);
			}
		}
		Map<String, Object> json = new HashMap<String, Object>();
		json.put("flag", 0);
		json.put("result", content);
		return new ApiResponse(json);
	}

	
	@SuppressWarnings("unchecked")
	private void requestToken() throws IOException {
		log.info("===requestToken===");
		Map<String, Object> json = new HashMap<String, Object>();
		json.put("deviceId", deviceId);
		Map<String, Object> content = (Map<String, Object>) rawPost("passport/requestToken",json,Crypt.NONE);
		token = (String) content.get("token");
		publicKey = (String) content.get("publicKey");
		signKey = (String) content.get("signKey");
		refreshToken = (String) content.get("refreshToken");
	}

	@SuppressWarnings("unchecked")
	public void login() throws IOException{
		log.info("===login===");
		Map<String, Object> json = new HashMap<String, Object>();
		json.put("phone", "18012345678");
		json.put("pwd", "123456");
		json.put("deviceId", deviceId);
		json.put("versionCode", 100);
		json.put("lat", 10.10);
		json.put("lng", 10.10);
		Map<String, Object> content = (Map<String, Object>) rawPost("passport/login", json, Crypt.NONE);
		content = (Map<String, Object>) content.get("token");
		refreshToken = (String) content.get("refreshToken");
	}
	
	private Object rawPost(String api,Map<String, Object> jsonObject ,int crypt) throws IOException{
		log.info(api + ":" + jsonObject);
		PostData postData = getPostData(jsonObject, crypt);
		HttpURLConnection connection = connect(baseUrl + api,postData.headers, postData.postDatao);
		return getResult(connection, crypt);
	}

	private PostData getPostData(Map<String, Object> jsonObject,int crypt) throws IOException{
		Map<String, String> headers = new HashMap<String, String>();
		headers.put("token", token);
		if(jsonObject==null){
			jsonObject = new HashMap<String, Object>();
		}
		byte[] postData = JsonUtil.toJson(jsonObject).getBytes("UTF-8");
		headers.put("totalLength", String.valueOf(postData.length));
		return new PostData(headers,postData);
	}
	
	private static class PostData{
		Map<String, String> headers;
		byte[] postDatao;
		
		public PostData(Map<String, String> headers,byte[] postData){
			this.headers = headers;
			this.postDatao = postData;
		}
	}
	
	private static HttpURLConnection connect(String url, Map<String, String> headers,byte[] content) throws IOException {
		HttpURLConnection connection = createConnection(url,headers,content);
		DataOutputStream out = null;
		try{
			//write
			if(content!=null){
				out = new DataOutputStream(connection.getOutputStream());
				out.write(content);
				out.flush();
			}
		}finally{
			if(out!=null)try{out.close();}catch (Exception e) {}
		}
		return connection;
	}
	
	private static HttpURLConnection createConnection(String url,Map<String, String> headers,byte[] content) throws IOException{
		URL postUrl = new URL(url);
		HttpURLConnection connection = (HttpURLConnection) postUrl.openConnection();
		connection.setDoOutput(true);
		connection.setDoInput(true);
		connection.setReadTimeout(10000);
		connection.setConnectTimeout(10000);
		connection.setRequestMethod("POST");
		if(headers!=null){
			for (Entry<String, String> entry : headers.entrySet()) {
				connection.setRequestProperty(entry.getKey(), entry.getValue());
			}
			
		}
		connection.setUseCaches(false);
		connection.setInstanceFollowRedirects(true);
		connection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
		connection.setRequestProperty("Content-Length", String.valueOf(content.length));
		connection.setChunkedStreamingMode(5);
		return connection;
	}
	
	private Object getResult(HttpURLConnection connection,int crypt) throws IOException{
		Object content = null;
		try {
			content = JsonUtil.readToObject(connection.getInputStream());
		} catch (IOException e) {
			if (connection.getResponseCode()==200 || connection.getResponseCode()==418) {
				content = JsonUtil.readToObject(connection.getErrorStream());
			} else {
				log.info(connection.getResponseCode()+":"+connection.getResponseMessage());
				Map<String, Object> json = new HashMap<String, Object>();
				json.put("http_status", connection.getResponseCode());
				content = json;
			}
		}
		return content;
	}
}
